home *** CD-ROM | disk | FTP | other *** search
Modula Implementation | 1994-09-22 | 21.8 KB | 636 lines |
- IMPLEMENTATION MODULE Stacks;
-
- (*****************************************************************************)
- (* Jeder Stack hat auch im Header seine eigene Adresse gespeichert, damit *)
- (* sichergestellt werden kann, dass mit einem definierten Stack gearbeitet *)
- (* wird. Um allerdings jedesmal die Adresse ueberpruefen zu koennen, muss der*)
- (* Stack immer als VAR-Parameter uebergeben werden, sonst kommt man nicht *)
- (* mehr an die Adresse ran. *)
- (* Wer will, kann ja die Adressenabfrage aus den Prozeduren entfernen. *)
- (* *)
- (* Zur Speicherverwaltung siehe Kopftext des "Queues"-Implementationsmoduls. *)
- (* Im Gegensatz zu einer Queue braucht beim Stack nur die Adresse des ober- *)
- (* sten Blocks bekannt zu sein, da auch nur von dieser Seite auf den Stack *)
- (* zugegriffen wird. *)
- (* *)
- (* *)
- (* stack oberster Block unterster Block *)
- (* voll *)
- (* | <---- *)
- (* | | ________________ ________________ *)
- (* ________V______|_ | | : : *)
- (* | stackAdr | | noch frei | : : *)
- (* |-----------------| |................| : belegt : *)
- (* : : -->| oberstes Elem | : : *)
- (* |-----------------| | |----------------| :----------------: *)
- (* | stackTop |-- : belegt : : unterstes Elem : *)
- (* |-----------------| |----------------| |----------------| *)
- (* | topBlock |----->| naechsterBlock |-~~~~>| NIL | *)
- (* |_________________| |________________| |________________| *)
- (* *)
- (* *)
- (*___________________________________________________________________________*)
- (* *)
- (* 03-Mai-89, hk *)
- (* Beginn, erste Version *)
- (* 29-Jul-89, hk *)
- (* SpezialRecord 'Adresse' entfallen, Rueckgabewerte von Proz., *)
- (* nicht gleich Abbruch *)
- (* 20-Aug-89, hk *)
- (* Rueckgabewerte nur noch BOOLEAN *)
- (* 04-Sep-89, hk *)
- (* Bei gelinktem Modul Abbruch mit Pterm0 *)
- (* 29-Dez-89, hk *)
- (* Ein paar kleinere ( rauesper ) Fehler beseitigt. *)
- (* Die Speicherplatzgroesse wird jetzt beim Einrichten des Stacks *)
- (* festgelegt, eine Fehlermeldung wegen falscher Groesse gibt es *)
- (* jetzt auch bei "TopOfStack" und "Pop". *)
- (* Der Stack enthaelt zur Konsistenzpruefung seine eigene Adresse *)
- (* und auch die Anzahl der auf dem Stack liegenden Elemente, die mit *)
- (* der neuen Funktion "Length" festgestellt werden kann. Es folgt *)
- (* Fehlermeldung und Programmabbruch, falls mit einem undefinierten *)
- (* Stack gearbeitet wird. Separater Errorhandler. *)
- (* Fuer das Kopieren der Stackelemente werden Prozeduren aus dem *)
- (* Modul "Memory" benutzt. *)
- (* Kuerzung der Namen ( Anhaengsel 'Stack' entfernt ), um eine *)
- (* Qualifizierung schmackhafter zu machen - ist uebersichtlicher. *)
- (* 30-Dez-89, hk *)
- (* Je nach Groesse der auf dem Stack abzulegenden Daten wird die *)
- (* entsprechende Kopierprozedur aus "Memory" schon beim Anlegen des *)
- (* Stacks ausgewaehlt. *)
- (* 23-Feb-90, hk *)
- (* voellig neue Block-Speicherverwaltung, automatischer Errorhandler *)
- (* <done> als Prozedurrueckgabe, extra Fehlerrueckgabe mit *)
- (* "LastStackResult" *)
- (*****************************************************************************)
-
-
- FROM SYSTEM IMPORT (* TYPE *) BYTE, ADDRESS,
- (* PROC *) VAL, INLINE, ADR, LONG;
-
- FROM HEAP IMPORT (* PROC *) Allocate, Deallocate; (* = Storage ?? *)
-
- IMPORT MEMORY; (* (* TYPE *) CopyProc,
- (* PROC *) ClearMem, CopySmallMem, CopyMem;
- *)
-
- (* =========================== T Y P E N ================================= *)
-
- TYPE
- block = POINTER TO block; (* ...schaut rekursiv aus, ist aber ok *)
-
-
- Stack = POINTER TO StackInfo;
-
- StackInfo = RECORD
- stackAdr : ADDRESS; (* Adresse eines Stacks *)
- Copy : MEMORY.CopyProc; (* Prozedur fuers Wertekopieren *)
- elemSize : CARDINAL; (* Groesse eines Stackelements *)
- maxElement : LONGINT; (* Max. Elementindex im Block *)
- blockSize : LONGINT; (* Groesse eines Speicherblocks *)
- Elemente : CARDINAL; (* Anzahl der Stackelemente *)
- topElement : LONGINT; (* Index des obersten Elementes *)
- (* innerhalb des obersten Blocks *)
- stackTop : ADDRESS; (* Adresse des obersten Elementes *)
- topBlock : block; (* Adresse des obersten Blocks *)
- END;
-
-
- (* ========================================================================= *)
- (* ===================== L O K A L ===================================== *)
-
- VAR
- lastResult : StackResult;
-
- Stackhandler : StackHandler;
- handlerOn : BOOLEAN;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE emptyStackHandler ((* EIN/ -- *) proc : ARRAY OF CHAR;
- (* EIN/ -- *) stErr : StackResult );
- (*T*)
- (* nur damit das System nicht abstuerzt, falls aus irgendeinem
- Grund der Handler aktiviert wird, obwohl keiner definiert wurde...
- *)
- BEGIN
- END emptyStackHandler;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE ReleaseBlock ((* EIN/AUS *) VAR stack : Stack );
- (*T*)
- (* Lokale Hilfsprozedur fuer "Delete", "Clear", "Drop" und "Pop".
- Entfernt ohne Sicherheitsabfrage den obersten Block vom Stack.
- Der Speicherplatz des Blocks wird freigegeben.
- *)
- VAR alterBlock : block;
-
- BEGIN
- WITH stack^ DO
- alterBlock := topBlock; (* Element muss referenzierbar *)
- (* bleiben *)
- topBlock := topBlock^; (* Element aus der Zeigerkette *)
- (* nehmen *)
- Deallocate( alterBlock, blockSize );
- END; (* WITH *)
- END ReleaseBlock;
-
-
- (* Ende LOKAL ============================================================== *)
-
- PROCEDURE LastStackResult ( ): StackResult;
- (*T*)
- BEGIN
- RETURN( lastResult );
- END LastStackResult;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE AssignStackHandler ((* EIN/ -- *) handler : StackHandler );
- (*T*)
- BEGIN
- Stackhandler := handler;
- handlerOn := TRUE;
- lastResult := stackOk;
- END AssignStackHandler;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE UnAssignStackHandler;
- (*T*)
- BEGIN
- handlerOn := FALSE;
- Stackhandler := emptyStackHandler;
- lastResult := stackOk;
- END UnAssignStackHandler;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE Create ((* EIN/ -- *) groesse : CARDINAL;
- (* EIN/ -- *) blkElem : CARDINAL;
- (* -- /AUS *) VAR stack : Stack;
- (* -- /AUS *) VAR done : BOOLEAN );
- (*T*)
- CONST procName = 'Create';
-
- BEGIN
- IF groesse = 0 THEN groesse := 1; END;
- IF blkElem = 0 THEN blkElem := 1; END;
-
- done := FALSE;
-
- Allocate( stack, SIZE( stack^ )); (* = NEW( stack ) *)
- IF stack # NIL THEN
-
- WITH stack^ DO
- blockSize := LONG( blkElem ) * LONG( groesse ) + LONG( SIZE( block ));
-
- Allocate( topBlock, blockSize );
- IF topBlock # NIL THEN
-
- (* Sowohl fuer den Stack-Header, als auch fuer
- * den ersten Block gabs genuegend Speicher.
- *)
-
- topBlock^ := NIL; (* letzter Block *)
-
- done := TRUE;
- lastResult := stackOk;
-
- IF groesse <= 10 THEN
- (* Bei weniger als 10 Bytes ist diese
- * Prozedur schneller, und ueberlappende
- * Speicherbereiche duerfte es hier eigent-
- * lich nicht geben.
- *)
- Copy := MEMORY.CopySmallMem;
- ELSE
- Copy := MEMORY.CopyMem;
- END; (* IF groesse *)
-
- stackAdr := ADR( stack ); (* Stack definiert *)
- elemSize := groesse;
- maxElement := VAL( LONGINT, blkElem - 1 );
- Elemente := 0;
- topElement := -1;
- stackTop := VAL( LONGINT, topBlock ) + LONG( SIZE( block ))
- - LONG( elemSize );
- END; (* IF topBlock *)
- END; (* WITH stack^ *)
- END; (* IF stack *)
-
- IF ~done THEN
- lastResult := noMem;
- stack := NIL;
-
- IF handlerOn THEN
- Stackhandler( procName, noMem );
- END;
- END; (* IF ~done *)
- END Create;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE Clear ((* EIN/AUS *) VAR stack : Stack;
- (* -- /AUS *) VAR done : BOOLEAN );
- (*T*)
- CONST procName = 'Clear';
-
- BEGIN
- IF ( stack # NIL ) & ( stack^.stackAdr = ADR( stack )) THEN
-
- WITH stack^ DO
- WHILE topBlock^ # NIL DO
-
- (* Alle evtl. vorhandenen Bloecke ausser dem
- * ersten entfernen.
- *)
- ReleaseBlock( stack );
- END;(* WHILE topBlock^ *)
-
- Elemente := 0;
- topElement := -1;
- stackTop := VAL( LONGINT, topBlock ) + LONG( SIZE( block ))
- - LONG( elemSize );
- done := TRUE;
- lastResult := stackOk;
- END; (* WITH stack^*)
-
- ELSE (* <stack> undefiniert *)
-
- done := FALSE;
- lastResult := defErr;
-
- IF handlerOn THEN
- Stackhandler( procName, defErr );
- END;
- END; (* stack # NIL... *)
- END Clear;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE Delete ((* EIN/AUS *) VAR stack : Stack;
- (* -- /AUS *) VAR done : BOOLEAN );
- (*T*)
- CONST procName = 'Delete';
-
- BEGIN
- IF ( stack # NIL ) & ( stack^.stackAdr = ADR( stack )) THEN
- Clear( stack, done );
-
- (* <done> ist nur dummy-Parameter, da auf undefinierten
- * Stack schon hier geprueft wurde.
- * Jetzt noch obersten Block und den Stackheader entfernen.
- *)
-
- Deallocate( stack^.topBlock, SIZE( stack^.blockSize ));
- Deallocate( stack, SIZE( stack^ ));
-
- stack := NIL;
-
- done := TRUE;
- lastResult := stackOk;
-
- ELSE (* <stack> undefiniert *)
-
- done := FALSE;
- lastResult := defErr;
-
- IF handlerOn THEN
- Stackhandler( procName, defErr );
- END;
- END;
- END Delete;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE IsEmpty ((* EIN/ -- *) VAR stack : Stack ): BOOLEAN;
- (*T*)
- CONST procName = 'IsEmpty';
-
- BEGIN
- IF ( stack # NIL ) & ( stack^.stackAdr = ADR( stack )) THEN
-
- lastResult := stackOk;
-
- RETURN( stack^.Elemente = 0 );
-
- ELSE (* <stack> undefiniert *)
-
- lastResult := defErr;
- IF handlerOn THEN
- Stackhandler( procName, defErr );
- END;
-
- RETURN( TRUE );
- END;
- END IsEmpty;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE Length ((* EIN/ -- *) VAR stack : Stack ): CARDINAL;
- (*T*)
- CONST procName = 'Length';
-
- BEGIN
- IF ( stack # NIL ) & ( stack^.stackAdr = ADR( stack )) THEN
-
- lastResult := stackOk;
-
- RETURN( stack^.Elemente );
-
- ELSE (* <stack> undefiniert *)
-
- lastResult := defErr;
- IF handlerOn THEN
- Stackhandler( procName, defErr );
- END;
-
- RETURN( 0 );
- END;
- END Length;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE Push ((* EIN/ -- *) wert : ARRAY OF BYTE;
- (* EIN/AUS *) VAR stack : Stack;
- (* -- /AUS *) VAR done : BOOLEAN );
- (*T*)
- CONST procName = 'Push';
-
- VAR neuerBlock : block;
-
- BEGIN
- done := FALSE; (* wird nur bei Erfog geaendert *)
- lastResult := defErr; (* wird je nach Fehler gesetzt *)
-
- WITH stack^ DO
- IF ( stack # NIL ) & ( stack^.stackAdr = ADR( stack )) THEN
-
- IF elemSize # VAL( CARDINAL, HIGH( wert )) + 1 THEN
-
- (* Der Speicherplatz eines Feldes von BYTES laesst
- * sich natuerlich aus der Obergrenze des Feldes
- * berechnen. Hier stimmt der Speicherbedarf nicht
- * mit der Definition ueberein.
- *)
- lastResult := sizeErr;
-
- ELSE (* Speicherplatz stimmt *)
-
- IF topElement < maxElement THEN
- (* Fuer das neue Element ist noch Platz im Block *)
-
- INC( topElement );
- INC( stackTop, elemSize );
-
- done := TRUE;
-
- ELSE (* neuer Block faellig *)
-
- (* Der Speicher fuer den neuen Block
- * wird beschafft.
- *)
- Allocate( neuerBlock, blockSize );
-
- IF neuerBlock = NIL THEN
- lastResult:= noMem; (* Kein Speicher mehr *)
-
- ELSE (* alles klar *)
-
- neuerBlock^ := topBlock; (* neuen Block einklinken *)
- topBlock := neuerBlock;
-
- topElement := 0;
- stackTop := VAL( ADDRESS, topBlock ) + LONG( SIZE( block ));
-
- done := TRUE;
- END; (* IF neuerBlock *)
- END; (* IF topElement *)
- END; (* IF elemSize *)
- END; (* IF stack # NIL *)
-
- IF done THEN
-
- Copy( ADR( wert ), stackTop, elemSize );
- INC( Elemente );
- lastResult := stackOk;
-
- ELSE (* Fehler aufgetreten *)
-
- IF handlerOn THEN
- Stackhandler( procName, lastResult );
- END;
- END; (* IF done *)
-
- END; (* WITH stack^ *)
-
- END Push;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE TopOfStack ((* EIN/ -- *) VAR stack : Stack;
- (* -- /AUS *) VAR wert : ARRAY OF BYTE;
- (* -- /AUS *) VAR done : BOOLEAN );
- (*T*)
- CONST procName = 'TopOfStack';
-
- BEGIN
- done := FALSE;
- lastResult := defErr;
-
- WITH stack^ DO
- IF ( stack # NIL ) & ( stack^.stackAdr = ADR( stack )) THEN
-
- IF elemSize # VAL( CARDINAL, HIGH( wert )) + 1 THEN
- lastResult := sizeErr;
-
- ELSE (* Speicherplatz stimmt *)
-
- IF Elemente = 0 THEN
- lastResult := stackEmpty; (* nix da *)
-
- ELSE (* Stack nicht leer *)
- done := TRUE;
- lastResult := stackOk;
-
- Copy( stackTop, ADR( wert ), elemSize );
- END; (* IF Elemente *)
- END; (* IF elemSize *)
- END;(* IF stack # NIL ... *)
- END; (* WITH stack^ *)
-
- IF ~done THEN
- (* Zur Sicherheit den gelieferten
- * ( nicht vorhandenen ) wert init.
- *)
- MEMORY.ClearMem( ADR( wert ), HIGH( wert ) + 1 );
-
- IF handlerOn THEN
- Stackhandler( procName, lastResult );
- END;
- END; (* IF ~done *)
-
- END TopOfStack;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE Drop ((* EIN/AUS *) VAR stack : Stack;
- (* -- /AUS *) VAR done : BOOLEAN );
- (*T*)
- CONST procName = 'Drop';
-
- BEGIN
- done := FALSE;
- lastResult := defErr;
-
- WITH stack^ DO
- IF ( stack # NIL ) & ( stack^.stackAdr = ADR( stack )) THEN
-
- IF Elemente > 0 THEN
- done := TRUE;
- lastResult := stackOk;
-
- DEC( Elemente );
-
- IF ( Elemente > 0 ) & ( topElement > 0D ) THEN
-
- (* Wenn noch ein Element auf dem Stack ist, und das
- * naechste Element noch innerhalb dieses Blocks ist,
- * koennen Index und Adresse einfach verringert werden.
- *)
-
- DEC( topElement );
- DEC( stackTop, elemSize );
-
- ELSE (* <stack> leer oder lediglich oberster Block leer *)
-
- IF topBlock^ # NIL THEN
-
- (* Wenn lediglich der oberste Block leer ist,
- * aber nicht der Stack - d.h. es existieren noch
- * weitere Bloecke -, den obersten Block entfernen.
- * Der Zeiger aufs oberste Element muss aufs
- * oberste Element des naechsten Blocks zeigen.
- *)
- ReleaseBlock ( stack );
-
- topElement := maxElement;
- stackTop := VAL( LONGINT, topBlock )
- + LONG( SIZE( block ))
- + maxElement * LONG( elemSize );
-
- ELSE (* der Stack ist leer *)
-
- topElement := -1;
- stackTop := VAL( ADDRESS, topBlock )
- - VAL( ADDRESS, elemSize )
- + LONG( SIZE( block ))
-
- END; (* IF topBlock^ *)
- END; (* IF ( Elemente > 0 )... *)
-
- ELSE (* IF Elemente > 0 *)
-
- lastResult := stackEmpty
- END; (* IF Elemente > 0 *)
- END; (* IF stack # NIL *)
- END; (* WITH stack^ *)
-
- IF ~done & handlerOn THEN
- Stackhandler( procName, lastResult );
- END;
-
- END Drop;
-
- (* ------------------------------------------------------------------------- *)
-
- PROCEDURE Pop ((* EIN/AUS *) VAR stack : Stack;
- (* -- /AUS *) VAR wert : ARRAY OF BYTE;
- (* -- /AUS *) VAR done : BOOLEAN );
- (*T*)
- CONST procName = 'Pop';
-
- BEGIN
- done := FALSE;
- lastResult := defErr;
-
- WITH stack^ DO
- IF ( stack # NIL ) & ( stack^.stackAdr = ADR( stack )) THEN
-
- IF elemSize # VAL( CARDINAL, HIGH( wert )) + 1 THEN
- lastResult := sizeErr;
-
- ELSE (* Speicherplatz stimmt *)
-
- IF Elemente = 0 THEN
- lastResult := stackEmpty;
-
- ELSE (* Stack nicht leer *)
- done := TRUE;
- lastResult := stackOk;
-
- Copy( stackTop, ADR( wert ), elemSize );
-
- DEC( Elemente );
-
- IF ( Elemente > 0 ) & ( topElement > 0D ) THEN
-
- (* Wenn noch ein Element auf dem Stack ist, und das
- * naechste Element noch innerhalb dieses Blocks ist,
- * koennen Index und Adresse einfach verringert werden.
- *)
-
- DEC( topElement );
- DEC( stackTop, elemSize );
-
- ELSE (* <stack> leer oder lediglich oberster Block leer *)
-
- IF topBlock^ # NIL THEN
-
- (* Wenn lediglich der oberste Block leer ist,
- * aber nicht der Stack - d.h. es existieren noch
- * weitere Bloecke -, den obersten Block entfernen.
- * Der Zeiger aufs oberste Element muss aufs
- * oberste Element des naechsten Blocks zeigen.
- *)
- ReleaseBlock ( stack );
-
- topElement := maxElement;
- stackTop := VAL( LONGINT, topBlock )
- + LONG( SIZE( block ))
- + maxElement * LONG( elemSize );
-
- ELSE (* der Stack ist leer *)
-
- topElement := -1;
- stackTop := VAL( ADDRESS, topBlock )
- - VAL( ADDRESS, elemSize )
- + LONG( SIZE( block ))
-
- END; (* IF topBlock^ *)
- END; (* IF ( Elemente > 0 )... *)
-
- END; (* IF Elemente = 0 *)
- END; (* IF elemSize *)
- END; (* IF stack # NIL ... *)
- END; (* WITH stack^ *)
-
- IF ~done THEN
- MEMORY.ClearMem( ADR( wert ), HIGH( wert ) + 1 );
-
- IF handlerOn THEN
- Stackhandler( procName, lastResult );
- END;
- END; (* IF ~done *)
-
- END Pop;
-
-
- END Stacks.
-
-
-
-
-